package core;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;

public class Calculator {

	static public Date beginOf(int year, int mounth){
		Calendar c = Calendar.getInstance();
		c.set(year, mounth, 1);
		return c.getTime();
	}

	static public Date endOf(int year, int mounth){
		Calendar c = GregorianCalendar.getInstance();
		c.set(year, mounth, 1);
		c.set(year, mounth, c.getActualMaximum(Calendar.DAY_OF_MONTH));
		return c.getTime();
	}

	private float balancePos(List<UserEntry> all) {
		Iterator<UserEntry> i = all.iterator();
		float pos = 0.0f;
		while (i.hasNext()) {
			UserEntry cur = i.next();
			if (cur.getValue() > 0)
				pos += cur.getValue();
		}
		return pos;
	}

	public float getBalancePos(Date since, Date until) {
		return balancePos(Core.get().getEntriesBetween(since, until));
	}

	public float getBalancePos(Date until) {
		return balancePos(Core.get().getEntriesUntil(until));
	}

	public float getBalancePos() {
		return balancePos(Core.get().getEntries());
	}

	private float balanceNeg(List<UserEntry> all) {
		Iterator<UserEntry> i = all.iterator();
		float neg = 0.0f;
		while (i.hasNext()) {
			UserEntry cur = i.next();
			if (cur.getValue() < 0)
				neg -= cur.getValue();
		}
		return neg;
	}

	public float getBalanceNeg(Date since, Date until) {
		return balanceNeg(Core.get().getEntriesBetween(since, until));
	}

	public float getBalanceNeg(Date until) {
		return balanceNeg(Core.get().getEntriesUntil(until));
	}

	public float getBalanceNeg() {
		return balanceNeg(Core.get().getEntries());
	}

	private float balance(List<UserEntry> all) {
		Iterator<UserEntry> i = all.iterator();
		float mid = 0.0f;
		while (i.hasNext()) {
			UserEntry cur = i.next();
			mid += cur.getValue();
		}
		return mid;
	}

	public float getBalance(Date since, Date until) {
		return balance(Core.get().getEntriesBetween(since, until));
	}

	public float getBalance(Date until) {
		return balance(Core.get().getEntriesUntil(until));
	}

	public float getBalance() {
		return balance(Core.get().getEntries());
	}

	private float balanceOf(int categoryId, List<UserEntry> all) {
		Iterator<UserEntry> i = all.iterator();
		float mid = 0.0f;
		while (i.hasNext()) {
			UserEntry cur = i.next();
			if (cur.getCategoryId() == categoryId)
				mid += cur.getValue();
		}
		return mid;
	}

	public float GetBalanceOf(Date since, Date until, int categoryId) {
		return balanceOf(categoryId, Core.get().getEntriesBetween(since, until));
	}

	public float GetBalanceOf(Date until, int categoryId) {
		return balanceOf(categoryId, Core.get().getEntriesUntil(until));
	}

	public float GetBalanceOf(int categoryId) {
		return balanceOf(categoryId, Core.get().getEntries());
	}

	public float balancePosOf(List<UserEntry> entries, int catId){
		float b = 0;		
		Iterator<UserEntry> i = entries.iterator();
		while (i.hasNext()) {
			UserEntry cur = i.next();
			if (cur.getCategoryId() == catId && cur.getValue() > 0)
				b += cur.getValue();
		}
		return b;
	}

	public float balanceNegOf(List<UserEntry> entries, int catId){
		float b = 0;		
		Iterator<UserEntry> i = entries.iterator();
		while (i.hasNext()) {
			UserEntry cur = i.next();
			if (cur.getCategoryId() == catId && cur.getValue() < 0)
				b -= cur.getValue();
		}
		return b;
	}

	public float[] getRatiosPos(Date since, Date until) {
		float[] ratios = new float[XmlEngine.get().getCategories().size() + 1];
		List<UserEntry> entries = Core.get().getEntriesBetween(since, until);
		float balancePos = balancePos(entries);

		Iterator<Category> i = XmlEngine.get().getCategories().iterator();
		int pos = 0;
		ratios[pos++] = balancePosOf(entries, 0) / balancePos;
		while (i.hasNext()) {
			Category cur = i.next();
			float balance = balancePosOf(entries, cur.getId());
			ratios[pos++] = balance / balancePos;
		}
		return ratios;
	}

	public float[] getRatiosNeg(Date since, Date until) {
		float[] ratios = new float[XmlEngine.get().getCategories().size() + 1];
		List<UserEntry> entries = Core.get().getEntriesBetween(since, until);
		float balanceNeg = balanceNeg(entries);

		Iterator<Category> i = XmlEngine.get().getCategories().iterator();
		int pos = 0;
		ratios[pos++] = balanceNegOf(entries, 0) / balanceNeg;
		while (i.hasNext()) {
			Category cur = i.next();
			float balance = balanceNegOf(entries, cur.getId());
			ratios[pos++] = balance / balanceNeg;
		}
		return ratios;
	}

	@SuppressWarnings("deprecation")
	private int mounthBetween(Date since, Date until) {
		int sM = since.getMonth();
		int sY = since.getYear();
		int uM = since.getMonth();
		int uY = since.getYear();
		return (uY - sY) * 12 + ((sM > uM) ? sM - uM : uM - sM);
	}

	private float [] PrepareTable(int mounthCount){
		float[] out = new float[mounthCount + 1];
		for (int i = 0; i < mounthCount; ++i)
			out[i] = 0;
		return out;
	}
		
	@SuppressWarnings("deprecation")
	private float[] expend(Date since, Date until, List<UserEntry> entries) {
		if (since.after(until))
			return null;

		int mounthCount = mounthBetween(since, until), curMounthStep = 0;
		int curMounth = since.getMonth(), curYear = since.getYear();
		float[] out = PrepareTable(mounthCount);

		Iterator<UserEntry> i = entries.iterator();
		while (i.hasNext()) {
			UserEntry cur = i.next();
			Date curDate = cur.getDate();
			if (curDate.getMonth() != curMounth || curDate.getYear() != curYear) {
				curMounthStep += mounthBetween(new Date(curYear, curMounth, 1),
						curDate);
				curMounth = curDate.getMonth();
				curYear = curDate.getYear();
			}
			if (cur.getValue() < 0)
				out[curMounthStep] -= cur.getValue();
		}
		return out;
	}

	public float[] getExpend(Date since, Date until) {
		return expend(since, until, Core.get().getEntriesBetween(since, until));
	}
	
	public float[] getExpendOf(Date since, Date until, int CategoryId) {
		return expend(since, until, Core.get().getEntriesOfBetween(CategoryId, since, until));
	}

	
	@SuppressWarnings("deprecation")
	private float[] income(Date since, Date until, List<UserEntry> entries) {
		if (since.after(until))
			return null;

		int mounthCount = mounthBetween(since, until), curMounthStep = 0;
		int curMounth = since.getMonth(), curYear = since.getYear();
		float[] out = PrepareTable(mounthCount);

		Iterator<UserEntry> i = entries.iterator();
		while (i.hasNext()) {
			UserEntry cur = i.next();
			Date curDate = cur.getDate();
			if (curDate.getMonth() != curMounth || curDate.getYear() != curYear) {
				curMounthStep += mounthBetween(new Date(curYear, curMounth, 1),
						curDate);
				curMounth = curDate.getMonth();
				curYear = curDate.getYear();
			}
			if (cur.getValue() > 0)
				out[curMounthStep] += cur.getValue();
		}
		return out;
	}

	public float[] getIncome(Date since, Date until) {
		return income(since, until, Core.get().getEntriesBetween(since, until));
	}
	
	public float[] getIncomeOf(Date since, Date until, int categoryId) {
		return income(since, until, Core.get().getEntriesOfBetween(categoryId, since, until));
	}
}
